/*
 *  engine.cpp
 *  
 *  Created by James Dykstra
 *	Team 5
 *
 *  Copyright 2010 Michigan Technological University. All rights reserved.
 */
#include "engine.h"
#include <iostream>
#include <time.h>

int netThread(void *data);
SDL_mutex *nlock = NULL;
SDL_mutex *olock = NULL;

GameEngine::GameEngine(const char *ip)
{
    renderer = new Renderer();
    nlock = SDL_CreateMutex();
    olock = SDL_CreateMutex();
    net = NULL;

    //net = new Network(ip);

    map= new Map("map4",renderer);
    logic = new Logic(renderer, map);
    sound = new Sound();
    player = new PlayerControlledShip(renderer, logic);
    for(int i=0; i<MAX_CLIENTS; i++)
        opponents[i] = new Opponent(renderer, logic);

    player->setMapWidth(map->getMapWidth());
    player->setMapHeight(map->getMapHeight());

    powerup = new Powerup(renderer);
    srandom((long) time(NULL));
    powerupCount = random()%POWERUP_DELAY_RANGE+POWERUP_DELAY_MIN;

    blackhole = new Blackhole(renderer, logic, sound, (map->getMapWidth()/2), (map->getMapHeight()/2));

    lastPos.x = -1;
    lastPos.y = -1;
    lastAngle = -1;

    showHitboxes = false;

    delta = new Timer();
    fps = new Timer();
    renderer->exit = false;
    networkset = false;

    delta->start();


    //set up the default controls+settings
    //key settings must correspond to the string output by
    //SDL_GetKeyName
    getSetting(&ipaddress,"ipaddress");
    if( ipaddress.length() == 0 )
        setSetting( &ipaddress, "ipaddress", "127.0.0.1");
    renderer->setIP( ipaddress);

    getSetting(&key_forward,"key_forward");
    if(  key_forward.length() == 0 )
        setSetting(&key_forward,"key_forward","up");

    getSetting(&key_backward,"key_backward");
    if(  key_backward.length() == 0 )
        setSetting(&key_backward,"key_backward","down");

    getSetting(&key_left,"key_left");
    if( key_left.length() == 0 )
        setSetting(&key_left,"key_left","left");

    getSetting(&key_right,"key_right");
    if( key_right.length() == 0 )
        setSetting(&key_right,"key_right","right");

    getSetting(&key_fire,"key_fire");
    if( key_fire.length() == 0 )
        setSetting(&key_fire,"key_fire","z");

    getSetting(&key_firewide,"key_firewide");
    if( key_firewide.length() == 0 )
        setSetting(&key_firewide,"key_firewide","x");

    getSetting(&key_special,"key_special");
    if( key_special.length() == 0 )
        setSetting(&key_special,"key_special","c");

    getSetting(&key_togglehitboxes,"key_togglehitboxes");
    if( key_togglehitboxes.length() == 0 )
        setSetting(&key_togglehitboxes,"key_togglehitboxes","h");


}
GameEngine::~GameEngine()
{
    if(sthread!=NULL)
        SDL_KillThread(sthread);
    if(nlock != NULL)
        SDL_DestroyMutex(nlock);
    if(olock != NULL)
        SDL_DestroyMutex(olock);
    if(net!=NULL)
        delete net;
    if(blackhole!=NULL)
        delete blackhole;
    for(int i=0; i<MAX_CLIENTS; i++)
        delete opponents[i];
    if(player!=NULL)
        delete player;
    if(sound != NULL)
        delete sound;
    if(logic != NULL)
        delete logic;
    if(fps != NULL)
        delete fps;
    if(delta != NULL)
        delete delta;
    if(map != NULL)
        delete map;
    if(renderer != NULL)
        delete renderer;
    if (powerup != NULL) 
        delete powerup;
}
void GameEngine::run()
{
    sound->playSound(SND_AMB,-1);
    while(renderer->exit == false )
    {
        if( renderer->ingame == true )
        {
            if( networkset == false )
            {
                //change the ip address to whatever the user entered, write it to the settings file
                //for next time


                ipaddress = renderer->getIP().c_str();
                setSetting(&ipaddress,"ipaddress",ipaddress.c_str());

                if( renderer->isserver )
                    net = new Network( NULL );
                else
                    net = new Network(ipaddress.c_str());
                net->setPlayer(player);
                net->setOpponents(opponents);
                net->setSound(sound);
                net->setPowerup(powerup);
                net->open();

                if(!net->isServer())
                    net->send("helo", 0, false);
                else
                {
                    for(int i=0; i<MAX_CLIENTS; i++)
                        opponents[i]->setClientNum((i+1));
                }

                sthread = SDL_CreateThread(netThread, net);

                networkset = true;
            }
            capFrameRate();
        }
        //Update positions of sprites and objects
        update();
        //Draw(in order): map, objects, enemies, players
        draw();
        //Handle input
        handleInput();
        //Send any potentially new data over network
        if( net != NULL )
            netUpdate();
        //Cap frame rate

    }
}
void GameEngine::netUpdate()
{
    if(net->connected() && net->getClientNum() != -1)
    {
        std::string buf = "";
        if(lastPos.x != player->getRect()->x)
        {
            buf += "X:";
            buf += Network::ProtocolParser::numToStr(player->getRect()->x);
            lastPos.x = player->getRect()->x;
        }
        if(lastPos.y != player->getRect()->y)
        {
            buf += "Y:";
            buf += Network::ProtocolParser::numToStr(player->getRect()->y);
            lastPos.y = player->getRect()->y;
        }
        if(lastAngle != player->getAngle())
        {
            buf += "A:";
            buf += Network::ProtocolParser::numToStr(player->getAngle());
            lastAngle = player->getAngle();
        }
        if(!buf.empty())
        {
            if(net->isServer())
                net->sendAll(buf, -1, true);
            else
                net->send(buf);
        }
    }
}
void GameEngine::update()
{
    fps->start();

    //moves all the map obstacles
    for(int i=0;i<map->getNumObstacles();i++){
        map->getObstacle(i)->move(delta->getTicks());
    }
    renderer->setHealth(player->getHP(),player->getMaxHP());

    if(player->getHP() > 0)
        player->moveFighter(delta->getTicks());

    if(blackhole->isActive())
        blackhole->dragPlayer(player);

    if ( net != NULL && net->isServer()) {
        if (powerup->isHidden()) {
            powerupCount--;
            if (powerupCount==0) {
                powerupCount = random()%POWERUP_DELAY_RANGE + POWERUP_DELAY_MIN;
                int powerupType = random()%3;
                int powerupX = random()%(map->getMapWidth());
                int powerupY = random()%(map->getMapHeight());
                powerup->init(powerupType, powerupX, powerupY);
                if (net->connected()) {
                    std::string tmp = "O:";
                    tmp += Network::ProtocolParser::numToStr(powerupType);
                    tmp += "X:";
                    tmp += Network::ProtocolParser::numToStr(powerupX);
                    tmp += "Y:";
                    tmp += Network::ProtocolParser::numToStr(powerupY);
                    net->sendAll(tmp, -1, false);

                }
            }
        } else {
            powerupCount--;
            if (powerupCount==0) {
                powerupCount = random()%500 + 500;
                powerup->hide();
                std::string tmp = "h:";
                tmp+=Network::ProtocolParser::numToStr(net->getClientNum());
                net->sendAll(tmp, -1, false);
            }
        }
    }


    if( net != NULL && net->connected())
    {
        if (!powerup->isHidden()) {
            if (logic->checkCollide(player, powerup)) {
                powerup->collect(player);
                std::string tmp = "h:";
                tmp+=Network::ProtocolParser::numToStr(net->getClientNum());
                if (net->isServer()) {
                    powerupCount = random()%POWERUP_DELAY_RANGE+POWERUP_DELAY_MIN;
                    net->sendAll(tmp, -1, false);
                } else {
                    net->send(tmp, 0, false);
                }

            }
        }
        for(int i=0; i<MAX_CLIENTS; i++)
        {
            if(opponents[i]->getClientNum() != -1)
            {
                SDL_mutexP(olock);
                opponents[i]->moveFighter(delta->getTicks());
                SDL_mutexV(olock);
                if(opponents[i]->getHP() > 0)
                {
                    if (!player->isInMercyMode()) 
                    {
                        SDL_mutexP(olock);
                        if(logic->checkCollide(player, opponents[i]))
                        {
                            SDL_mutexV(olock);
                            if (player->isShielded()) {
                                sound->playSound(SND_SHIELD, 0);
                                player->setShielded(false);
                            } else {
                                sound->playSound(SND_EXPLOS, 0);
                                player->dmg(opponents[i]->getMaxHP()/10);
                                player->setMercy(2.0);
                            }
                        }
                        else
                            SDL_mutexV(olock);
                    }
                }
                for(int b=0; b<BEAM_LIMIT; b++)
                {
                    if(opponents[i]->getBeam(b) != NULL)
                    {
                        if(opponents[i]->getBeam(b)->isActive() && player->getHP()>0 && !player->isInMercyMode())
                        {
                            SDL_mutexP(olock);
                            if(logic->checkCollide(player, opponents[i]->getBeam(b))) {
                                SDL_mutexV(olock);
                                std::string tmp = "H:";
                                tmp += Network::ProtocolParser::numToStr(opponents[i]->getClientNum());
                                tmp += "B:";
                                tmp += Network::ProtocolParser::numToStr(opponents[i]->getBeam(b)->getID());
                                if (player->isShielded()) {
                                    tmp+="S:1";
                                }
                                if(net->isServer())
                                    net->sendAll(tmp, -1, true);
                                else
                                    net->send(tmp);

                                if (player->isShielded()) {
                                    sound->playSound(SND_SHIELD, 0);
                                    player->setShielded(false);
                                } else {
                                    sound->playSound(SND_EXPLOS, 0);
                                    player->dmg(opponents[i]->getBeam(b)->getDamage());

                                }
                                opponents[i]->getBeam(b)->setActive(false);
                                if(player->getHP() <= 0)
                                {
                                    if (!player->respawn()) {
                                        tmp = "D:";
                                        tmp += Network::ProtocolParser::numToStr(net->getClientNum());
                                        if(net->isServer())
                                            net->sendAll(tmp, -1, false);
                                        else
                                            net->send(tmp, 0, false);
                                    }
                                }
                                else
                                    player->setMercy(2.0);
                            }
                            else
                                SDL_mutexV(olock);
                        }
                    }
                }
            }
        }
    }
    //Restart elapsed ticks timer
    delta->start();
}
void GameEngine::draw()
{

    //the x and y coordinates of the upper left corner of the screen on the map
    int mapDrawX=0;
    int mapDrawY=0;

    if(map!=NULL){
        if(player->getRect()->x+(player->getRect()->w/2)<(renderer->getScreenWidth()/2)){
            mapDrawX=0;
        }
        else if(player->getRect()->x+(player->getRect()->w/2)>(map->getMapWidth()-(renderer->getScreenWidth()/2))){
            mapDrawX=map->getMapWidth()-renderer->getScreenWidth();
        }
        else{
            mapDrawX=player->getRect()->x+(player->getRect()->w/2)-(renderer->getScreenWidth()/2);
        }
        if(player->getRect()->y+(player->getRect()->h/2)<(renderer->getScreenHeight()/2)){
            mapDrawY=0;
        }
        else if(player->getRect()->y+(player->getRect()->h/2)>(map->getMapHeight()-(renderer->getScreenHeight()/2))){
            mapDrawY=map->getMapHeight()-renderer->getScreenHeight();
        }
        else{
            mapDrawY=player->getRect()->y+(player->getRect()->h/2)-(renderer->getScreenHeight()/2);
        }
        map->drawMap(mapDrawX,mapDrawY);
    }

    if(blackhole->isActive())
        blackhole->show(mapDrawX, mapDrawY);

    if( net != NULL && net->connected())
    {
        for(int i=0; i<MAX_CLIENTS; i++)
        {
            if(opponents[i]->getHP() > 0 && opponents[i]->getClientNum() != -1)
            {
                SDL_mutexP(olock);
                opponents[i]->show(mapDrawX,mapDrawY);
                SDL_mutexV(olock);
            }
        }
    }

    if (!powerup->isHidden()) {
        powerup->show(mapDrawX, mapDrawY);
    }

    if(player->getHP() > 0)
        player->show(mapDrawX,mapDrawY);

    if(showHitboxes)
    {
        for(int i=0; i<map->getNumObstacles(); i++)
            if(map->getObstacle(i)->getHitbox()->getWorldCoord(0) != 0)
                renderer->drawBox(map->getObstacle(i)->getHitbox()->getWorldCoord(0)->getX()-mapDrawX, map->getObstacle(i)->getHitbox()->getWorldCoord(0)->getY()-mapDrawY, map->getObstacle(i)->getHitbox()->getWorldCoord(1)->getX()-mapDrawX, map->getObstacle(i)->getHitbox()->getWorldCoord(1)->getY()-mapDrawY, map->getObstacle(i)->getHitbox()->getWorldCoord(2)->getX()-mapDrawX, map->getObstacle(i)->getHitbox()->getWorldCoord(2)->getY()-mapDrawY, map->getObstacle(i)->getHitbox()->getWorldCoord(3)->getX()-mapDrawX, map->getObstacle(i)->getHitbox()->getWorldCoord(3)->getY()-mapDrawY);

        for(int i=0; i<BEAM_LIMIT; i++)
            if(player->getBeam(i)->isActive() && player->getBeam(i)->getHitbox()->getWorldCoord(0) != 0)
                renderer->drawBox(player->getBeam(i)->getHitbox()->getWorldCoord(0)->getX()-mapDrawX, player->getBeam(i)->getHitbox()->getWorldCoord(0)->getY()-mapDrawY, player->getBeam(i)->getHitbox()->getWorldCoord(1)->getX()-mapDrawX, player->getBeam(i)->getHitbox()->getWorldCoord(1)->getY()-mapDrawY, player->getBeam(i)->getHitbox()->getWorldCoord(2)->getX()-mapDrawX, player->getBeam(i)->getHitbox()->getWorldCoord(2)->getY()-mapDrawY, player->getBeam(i)->getHitbox()->getWorldCoord(3)->getX()-mapDrawX, player->getBeam(i)->getHitbox()->getWorldCoord(3)->getY()-mapDrawY);

        if(player->getHitbox()->getWorldCoord(0) != 0)
            renderer->drawBox(player->getHitbox()->getWorldCoord(0)->getX()-mapDrawX, player->getHitbox()->getWorldCoord(0)->getY()-mapDrawY, player->getHitbox()->getWorldCoord(1)->getX()-mapDrawX, player->getHitbox()->getWorldCoord(1)->getY()-mapDrawY, player->getHitbox()->getWorldCoord(2)->getX()-mapDrawX, player->getHitbox()->getWorldCoord(2)->getY()-mapDrawY, player->getHitbox()->getWorldCoord(3)->getX()-mapDrawX, player->getHitbox()->getWorldCoord(3)->getY()-mapDrawY);

        if(powerup->getHitbox()->getWorldCoord(0) != 0)
            renderer->drawBox(powerup->getHitbox()->getWorldCoord(0)->getX()-mapDrawX, powerup->getHitbox()->getWorldCoord(0)->getY()-mapDrawY, powerup->getHitbox()->getWorldCoord(1)->getX()-mapDrawX, powerup->getHitbox()->getWorldCoord(1)->getY()-mapDrawY, powerup->getHitbox()->getWorldCoord(2)->getX()-mapDrawX, powerup->getHitbox()->getWorldCoord(2)->getY()-mapDrawY, powerup->getHitbox()->getWorldCoord(3)->getX()-mapDrawX, powerup->getHitbox()->getWorldCoord(3)->getY()-mapDrawY);
    }

    renderer->update();

}

bool GameEngine::handleInput(){


    //update the keyevent variable
    while(SDL_PollEvent(&keyevent))
    {
        //stops the mouse from taking input after the game starts
         if( renderer->ingame == false  && keyevent.type == SDL_MOUSEMOTION )
         {
                    CEGUI::System::getSingleton().injectMousePosition(static_cast<float>(keyevent.motion.x),static_cast<float>(keyevent.motion.y) );
                    
                    if( renderer->ingame == true )
                        printf(" wtf\n");
                    break;

         }

         if( renderer->ingame == false  && keyevent.type == SDL_MOUSEBUTTONDOWN )
         {
                switch(keyevent.button.button) {
                    // handle real mouse buttons
                    case SDL_BUTTON_LEFT:
                        CEGUI::System::getSingleton().injectMouseButtonDown(CEGUI::LeftButton);
                        break;
                    case SDL_BUTTON_MIDDLE:
                        CEGUI::System::getSingleton().injectMouseButtonDown(CEGUI::MiddleButton);
                        break;
                    case SDL_BUTTON_RIGHT:
                        CEGUI::System::getSingleton().injectMouseButtonDown(CEGUI::RightButton);
                        break;

                        // handle the mouse wheel
                    case SDL_BUTTON_WHEELDOWN:
                        CEGUI::System::getSingleton().injectMouseWheelChange( -1 );
                        break;
                    case SDL_BUTTON_WHEELUP:
                        CEGUI::System::getSingleton().injectMouseWheelChange( +1 );
                        break;
                }
                break;
         }

         if( renderer->ingame == false  && keyevent.type == SDL_MOUSEBUTTONUP )
         {
                switch(keyevent.button.button) {
                    case SDL_BUTTON_LEFT:
                        CEGUI::System::getSingleton().injectMouseButtonUp(CEGUI::LeftButton);
                        break;
                    case SDL_BUTTON_MIDDLE:
                        CEGUI::System::getSingleton().injectMouseButtonUp(CEGUI::MiddleButton);
                        break;
                    case SDL_BUTTON_RIGHT:
                        CEGUI::System::getSingleton().injectMouseButtonUp(CEGUI::RightButton);
                        break;
                }
                break;
         }

        switch(keyevent.type){
            case SDL_QUIT:
                renderer->exit = true;
                break;
            case SDL_KEYDOWN:

                CEGUI::System::getSingleton().injectKeyDown(keyevent.key.keysym.sym);

                //inject backspace if it is a backspace, otherwise, inject a character
                if(SDLK_BACKSPACE == keyevent.key.keysym.sym)
                {
                    CEGUI::System::getSingleton().injectKeyDown(CEGUI::Key::Backspace);
                }
                else
                {
                    CEGUI::System::getSingleton().injectChar(keyevent.key.keysym.unicode);
                }


                //tie each 
                if( !strcmp(SDL_GetKeyName(keyevent.key.keysym.sym),key_togglehitboxes.c_str()))
                {
                    if( showHitboxes == true )
                    {
                        showHitboxes = false;
                    }
                    else if( showHitboxes == false )
                    {
                        showHitboxes = true;
                    }
                }

                if( !strcmp(SDL_GetKeyName(keyevent.key.keysym.sym),key_left.c_str()))
                    player->turnLeft();

                else if( !strcmp(SDL_GetKeyName(keyevent.key.keysym.sym),key_right.c_str()))
                    player->turnRight();

                else if( !strcmp(SDL_GetKeyName(keyevent.key.keysym.sym),key_forward.c_str()))
                    player->accelerateForward();

                else if( !strcmp(SDL_GetKeyName(keyevent.key.keysym.sym),key_backward.c_str()))
                    player->accelerateBackward();


                else if( !strcmp(SDL_GetKeyName(keyevent.key.keysym.sym),key_fire.c_str()))
                {
                    if (player->getNormalAttack()->fire())
                    {
                        sound->playSound(SND_LASER,0);
                        if(net != NULL)
                        {
                            if(net->connected())
                                net->send("F:-2");
                        }
                    }
                }

                else if( !strcmp(SDL_GetKeyName(keyevent.key.keysym.sym),key_firewide.c_str()))
                {
                    if (player->getWideAttack()->fire())
                    {
                        sound->playSound(SND_LASER,0);
                        if(net != NULL)
                        {
                            if(net->connected())
                                net->send("F:-3");
                        }
                    }
                }

                else if( !strcmp(SDL_GetKeyName(keyevent.key.keysym.sym),key_special.c_str()))
                {

                    if (player->getSpecialAttack()->fire())
                    {
                        sound->playSound(SND_LASER,0);
                        if(net != NULL)
                        {
                            if(net->connected())
                                net->send("F:-4");
                        }
                    }
                }

                switch(keyevent.key.keysym.sym){
                    case SDLK_ESCAPE:
                        renderer->exit = true;
                        if( net != NULL && net->connected())
                        {
                            if(net->isServer())
                                net->sendAll("Q:0", -1, false);
                            else
                            {
                                std::string goodbye = "Q:";
                                goodbye += Network::ProtocolParser::numToStr(net->getClientNum());
                                net->send(goodbye, 0, false);
                            }
                        }
                        break;
                    default:
                        break;
                }
                break;
            case SDL_KEYUP:
                CEGUI::System::getSingleton().injectKeyUp(keyevent.key.keysym.unicode);
                if( !strcmp(SDL_GetKeyName(keyevent.key.keysym.sym),key_left.c_str()) ||
                        !strcmp(SDL_GetKeyName(keyevent.key.keysym.sym),key_right.c_str()))
                    player->stopTurn();
                else if( !strcmp(SDL_GetKeyName(keyevent.key.keysym.sym),key_forward.c_str()) ||
                        !strcmp(SDL_GetKeyName(keyevent.key.keysym.sym),key_backward.c_str()))
                    player->brake();
                break;




        }
        return true;
    }

    return true;

}
void GameEngine::capFrameRate()
{
    //If there is time to spare after processing/drawing the frame, regulate.
    if(fps->getTicks()<(1000/FPS))
    {
        CEGUI::System::getSingleton().injectTimePulse( (1000/FPS) - fps->getTicks());
        SDL_Delay((1000/FPS) - fps->getTicks());
    }
}

/**
 * Read settings from the settings.xml file. If the setting does not exist,
 * it returns NULL.
 * @param settingname name of the setting whose value will be read.
 * @returns A string representing the value of the setting
 */
void GameEngine::getSetting( std::string * settingreturn, std::string settingname ) {

    struct stat fileinfo;
    std::string settingspath(getpwuid(getuid())->pw_dir);
    settingspath = settingspath + "/settings.xml";
    TiXmlDocument doc( settingspath.c_str() );

    if( stat(settingspath.c_str(), &fileinfo ) )
    {
        TiXmlDeclaration * decl = new TiXmlDeclaration( "1.0", "", "" );
        doc.LinkEndChild(decl);
        doc.SaveFile();
    }


    if( !doc.LoadFile() ) {
        fprintf( stderr, "err: settings file failed to read setting %s with error %s!\n", settingname.c_str(),doc.ErrorDesc() );
    }

    TiXmlHandle hDoc(&doc);

    TiXmlElement* pElem = hDoc.FirstChildElement( settingname.c_str() ).Element();


    if( pElem != NULL )
        settingreturn->assign(pElem->GetText());
    else
        settingreturn->assign("");

}

void GameEngine::getSetting( std::string * settingreturn, const char * settingname ) {

    struct stat fileinfo;
    std::string settingspath(getpwuid(getuid())->pw_dir);
    settingspath = settingspath + "/settings.xml";
    TiXmlDocument doc( settingspath.c_str() );

    if( stat(settingspath.c_str(), &fileinfo ) )
    {
        TiXmlDeclaration * decl = new TiXmlDeclaration( "1.0", "", "" );
        doc.LinkEndChild(decl);
        doc.SaveFile();
    }

    if( !doc.LoadFile() ) {
        fprintf( stderr, "err: settings file failed to read setting %s with error %s!\n", settingname, doc.ErrorDesc() );
    }

    TiXmlHandle hDoc(&doc);

    TiXmlElement* pElem = hDoc.FirstChildElement( settingname ).Element();

    if( pElem != NULL )
        settingreturn->assign(pElem->GetText());
    else
        settingreturn->assign("");

}
/**
 * Write to settings.xml file.  If the setting exists, it is overwritten.
 * The first parameter is the setting that is to be created/changed, and the 
 * second is the value for the setting.
 * @settingname name of the element that is the setting
 * @settingvalue value of the element that is the setting
 */
void     GameEngine::setSetting( std::string * settingreturn, std::string settingname, std::string settingvalue ) {


    struct stat fileinfo;
    std::string settingspath(getpwuid(getuid())->pw_dir);
    settingspath = settingspath + "/settings.xml";

    TiXmlDocument doc( settingspath.c_str() );

    if( stat(settingspath.c_str(), &fileinfo ) )
    {
        TiXmlDeclaration * decl = new TiXmlDeclaration( "1.0", "", "" );
        doc.LinkEndChild(decl);
        doc.SaveFile();
    }

    if( !doc.LoadFile() ) {
        fprintf( stderr, "err: settings file failed write setting %s with error %s!\n", settingname.c_str(), doc.ErrorDesc() );
        doc.ClearError();
    }

    //open a handler for easier navigation of entries
    TiXmlHandle hDoc(&doc);

    //grab the element for the listed setting
    TiXmlElement* pElem = hDoc.FirstChildElement( settingname.c_str() ).Element();

    //if it exists, clear it, and recreate it
    if ( pElem ) {
        doc.RemoveChild( pElem );
    }

    //create the new element
    pElem = new TiXmlElement( settingname.c_str() );


    //add it to the opened document
    doc.LinkEndChild( pElem );


    //replace / insert new value into the new element
    pElem->LinkEndChild( new TiXmlText( settingvalue.c_str() ) );

    //save the settings
    doc.SaveFile();
    settingreturn->assign(settingvalue);


}

void     GameEngine::setSetting( std::string * settingreturn, const char * settingname, const char * settingvalue ) {

    struct stat fileinfo;
    std::string settingspath(getpwuid(getuid())->pw_dir);
    settingspath = settingspath + "/settings.xml";

    TiXmlString settingstring(settingname);

    TiXmlDocument doc( settingspath.c_str() );

    if( stat(settingspath.c_str(), &fileinfo ) )
    {
        TiXmlDeclaration * decl = new TiXmlDeclaration( "1.0", "", "" );
        doc.LinkEndChild(decl);
        doc.SaveFile();
    }


    if( !doc.LoadFile() ) {
        fprintf( stderr, "err: settings file failed to write setting %s with error %s!\n", settingname,doc.ErrorDesc() );
        doc.ClearError();
    }

    //open a handler for easier navigation of entries
    TiXmlHandle hDoc(&doc);

    //grab the element for the listed setting
    TiXmlElement* pElem = hDoc.FirstChildElement( settingstring.c_str() ).Element();

    //if it exists, clear it, and recreate it
    if ( pElem ) {
        doc.RemoveChild( pElem );
    }

    //create the new element
    pElem = new TiXmlElement( settingstring.c_str() );


    //add it to the opened document
    doc.LinkEndChild( pElem );


    //replace / insert new value into the new element
    pElem->LinkEndChild( new TiXmlText( settingvalue ) );

    //save the settings
    doc.SaveFile();

    settingreturn->assign(settingvalue);

}

//netThread
//
//Run in its own thread.
//Given a pointer to a valid network object (data)
//Using the network object this thread watches for data being sent.
//For testing/demo purposes all received data is written to recv.txt
int netThread(void *data)
{
    Network *net = (Network*)data;
    std::string buffer = "";
    Opponent *controlled;
    std::fstream in("recv.txt", std::fstream::out | std::fstream::trunc);
    bool run = true;
    while(run)
    {
        SDL_mutexP(nlock);
        UDPpacket *p = net->recvPacket();
        buffer = net->extractData(p);
        SDL_mutexV(nlock);
        if(buffer.length()>0)
        {
            in<<buffer.c_str()<<std::endl;

            if(strcmp(buffer.c_str(), "helo")==0)
            {
                if(net->isServer())
                {
                    std::fstream log("log.txt", std::fstream::out | std::fstream::app);
                    SDL_mutexP(nlock);
                    if(net->newPlayerConnected(p))
                        log<<"Client #"<<net->getCur()<<": Connected."<<std::endl;
                    SDL_mutexV(nlock);
                    log.close();
                }
            }
            else
            {
                int num = Network::ProtocolParser::extractiVal(buffer, 'N');
                if(num != -1)
                {
                    SDL_mutexP(nlock);
                    net->setClientNum(num);
                    SDL_mutexV(nlock);
                    switch(num)
                    {
                        case 1:
                            net->getPlayer()->setPos((RES_WIDTH - net->getPlayer()->getRect()->w), net->getPlayer()->getRect()->h);
                            break;
                        case 2:
                            net->getPlayer()->setPos(net->getPlayer()->getRect()->w, (RES_HEIGHT - net->getPlayer()->getRect()->h));
                            break;
                        case 3:
                            net->getPlayer()->setPos((RES_WIDTH - net->getPlayer()->getRect()->w), (RES_HEIGHT - net->getPlayer()->getRect()->h));
                            break;
                        case 4:
                            net->getPlayer()->setPos(net->getPlayer()->getRect()->w, net->getPlayer()->getRect()->h);
                            break;
                        default:
                            break;
                    }
					net->getPlayer()->setRespawnPoint();
                    if(!net->isServer())
                    {
                        std::string reqUp = "U:";
                        reqUp += Network::ProtocolParser::numToStr(net->getClientNum());
                        net->send(reqUp, 0, false);
                    }
                }
                else
                {
                    int  q = Network::ProtocolParser::extractiVal(buffer, 'Q');
                    int u = Network::ProtocolParser::extractiVal(buffer, 'U');
                    int d = Network::ProtocolParser::extractiVal(buffer, 'D');
                    int o = Network::ProtocolParser::extractiVal(buffer, 'O');
                    int h = Network::ProtocolParser::extractiVal(buffer, 'h');
                    if(q != -1)
                    {
                        if(net->isServer())
                            net->sendAll(buffer, q, false);
                        else if(q == 0)
                        {
                            //Server shutdown!
                            std::fstream log("log.txt", std::fstream::out | std::fstream::app);
                            log<<"ERROR: Server shutdown!\n";
                            log.close();
                            net->close();
                            return 0;
                        }

                        SDL_mutexP(nlock);
                        net->removeClient(q);
                        SDL_mutexV(nlock);
                    }
                    else if(u != -1)
                    {
                        SDL_mutexP(nlock);
                        net->updateOpponents(u);
                        SDL_mutexV(nlock);
                    }
                    else if(d != -1)
                    {
                        controlled = net->getOpponent(d);
                        if(controlled != NULL)
                        {
                            if(net->isServer())
                                net->sendAll(buffer, d, false);

                            controlled->setHP(0);
                            std::fstream log("log.txt", std::fstream::out | std::fstream::app);
                            log<<"Player #"<<d<<" has died!\n";
                            log.close();
                        }
                    }
                    else if (o!=-1) {
                        Powerup *powerup = net->getPowerup();
                        int powerupX = Network::ProtocolParser::extractiVal(buffer, 'X');
                        int powerupY = Network::ProtocolParser::extractiVal(buffer, 'Y');
                        powerup ->init(o, powerupX, powerupY);

                    }
                    else if (h!=-1) {
                        Powerup *powerup = net->getPowerup();
                        powerup->hide();
                        if (net->isServer()) {
                            net->sendAll(buffer, h, false);
                        }
                    }


                    else
                    {
                        int cNum = Network::ProtocolParser::extractiVal(buffer, 'P');
                        if((cNum != net->getClientNum()))
                        {
                            if(net->isServer())
                                net->sendAll(buffer, -1, false);

                            controlled = net->getOpponent(cNum);

                            if(!net->isServer())
                            {
                                if(controlled->getClientNum() != cNum)
                                {
                                    SDL_mutexP(olock);
                                    controlled->setClientNum(cNum);
                                    controlled->setHP(100);
                                    SDL_mutexV(olock);

                                    std::fstream log("log.txt", std::fstream::out | std::fstream::app);
                                    log<<"Client #"<<net->getCur()<<": Joined."<<std::endl;
                                    log.close();
                                }
                            }

                            int f = Network::ProtocolParser::extractiVal(buffer, 'F');
                            if(f != -1)
                            {
                                SDL_mutexP(olock);
                                controlled->action(f);
                                SDL_mutexV(olock);
                            }
                            else
                            {
                                int s = Network::ProtocolParser::extractiVal(buffer, 'H');
                                if(s>=0 && s<=MAX_CLIENTS)
                                {
                                    if(controlled != NULL)
                                    {
                                        int b = Network::ProtocolParser::extractiVal(buffer, 'B');
                                        if(b != -1)
                                        {
                                            Fighter *attacker = NULL;
                                            if(s == net->getClientNum())
                                                attacker = net->getPlayer();
                                            else
                                                attacker = net->getOpponent(s);

                                            if(attacker->getBeam(b) != NULL)
                                            {
                                                SDL_mutexP(olock);
                                                controlled->dmg(attacker->getBeam(b)->getDamage());
                                                SDL_mutexV(olock);
                                                controlled->setMercy(2.0);
                                                attacker->getBeam(b)->setActive(false);
                                                if (Network::ProtocolParser::extractiVal(buffer, 'S')!=-1) {
                                                    net->getSound()->playSound(SND_SHIELD, 0);
                                                } else
                                                    net->getSound()->playSound(SND_EXPLOS, 0);
                                            }
                                        }
                                    }
                                }

                                int val = Network::ProtocolParser::extractiVal(buffer, 'X');
                                if(val != -1)
                                {
                                    if(controlled != NULL)
                                    {
                                        SDL_mutexP(olock);
                                        controlled->changeX(val);
                                        SDL_mutexV(olock);
                                    }
                                }

                                val = Network::ProtocolParser::extractiVal(buffer, 'Y');
                                if(val != -1)
                                {
                                    if(controlled != NULL)
                                    {
                                        SDL_mutexP(olock);
                                        controlled->changeY(val);
                                        SDL_mutexV(olock);
                                    }
                                }

                                float v = Network::ProtocolParser::extractfVal(buffer, 'A');
                                if(v != -1)
                                {
                                    if(controlled != NULL)
                                    {
                                        SDL_mutexP(olock);
                                        controlled->changeA(v);
                                        SDL_mutexV(olock);
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
        else
            SDLNet_FreePacket(p);
    }

    in.close();
    SDL_mutexP(nlock);
    net->close();
    SDL_mutexV(nlock);

    return 0;
}

